home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / t3_1 / nexttsrc.lha / nexttsources / sources / sys / bignum.t < prev    next >
Text File  |  1990-06-19  |  11KB  |  325 lines

  1. (herald bignum
  2.   (env tsys (osys fixnum) bignum))
  3.  
  4. ;;; (c) Copyright 1983, 1984 Yale University
  5.  
  6. ;;; To do:
  7. ;;;   destructive routines
  8. ;;;   rewrite print-bignum & associates (format nil ... etc)
  9. ;;;   pool bignums
  10. ;;;   bignum templates, positve & negative ?
  11. ;;;   pack densely - use hardware multiply, etc
  12.  
  13. ;;; WARNING
  14. ;;;  Parts of this depend on (>= *bits-per-hyperdigit* *bits-per-fixnum*) => T.
  15.  
  16. ;;; Constants:
  17.  
  18. (define-constant *bits-per-hyperdigit* 30)
  19.  
  20. (define-constant *max-hyperdigit* -1)
  21.  
  22. (define-constant *half-max-hyperdigit*
  23.   (fx- (fixnum-ashl 1 (fx- *bits-per-hyperdigit* 1)) 1))
  24.  
  25. (lset *bignums-print-nicely?* nil)
  26.  
  27. (define-handler bignum
  28.   (object nil
  29.     ((extended-number-type self) %%bignum-number-type)
  30.     ((print self port)
  31.      (if *bignums-print-nicely?*
  32.          (print-bignum self port)
  33.          (print-bignum-guts self port)))))
  34.  
  35. (define (print-bignum-guts self port)
  36.   (format port "{Bignum ~D ~A["
  37.           (object-hash self) 
  38.           (if (bignum-positive? self) "+" "-"))
  39.   (format port "~X" (bignum-digit self 0))
  40.   (do ((i 1 (fx+ i 1)))
  41.       ((fx>= i (bignum-length self)))
  42.     (format port "~_~X" (bignum-digit self i)))
  43.   (format port "]}"))
  44.  
  45. ;;; Comparison:
  46.  
  47. (define-integrable (bignum-magnitude-less? u v)
  48.   (fx< (bignum-compare-magnitudes u v) 0))
  49.  
  50. ;;; Returns a fixnum whose sign is the same as (- u v).
  51.  
  52. (define (bignum-compare u v)
  53.   (let ((u-sign (bignum-sign u)))
  54.     (cond ((fxn= u-sign (bignum-sign v)) u-sign)
  55.           ((fx> u-sign 0) (bignum-compare-magnitudes u v))
  56.           (else           (bignum-compare-magnitudes v u)))))
  57.  
  58. (define-integrable (bignum-less? u v)
  59.   (fx< (bignum-compare u v) 0))
  60.  
  61. (define-integrable (bignum-equal? u v)
  62.   (fx= (bignum-compare u v) 0))
  63.  
  64. ;;; Sign negotiation and normalization:
  65.  
  66. ;;; The BIGNUM-FOO routines negotiate a sign for the result, then
  67. ;;; dispatch to the appropriate FOO-MAGNITUDES routine.  The result
  68. ;;; is then normalized.
  69.  
  70. (define (bignum-add u v)
  71.   (let ((u-sign (bignum-sign u))
  72.         (v-sign (bignum-sign v)))
  73.     (normalize-integer
  74.      (cond ((fx= u-sign v-sign)
  75.             (set-bignum-sign! (add-magnitudes u v) u-sign))
  76.            ((bignum-magnitude-less? u v)
  77.             (set-bignum-sign! (subtract-magnitudes v u) v-sign))
  78.            (else
  79.             (set-bignum-sign! (subtract-magnitudes u v) u-sign))))))
  80.  
  81. (define (bignum-subtract u v)
  82.   (let ((u-sign (bignum-sign u))
  83.         (v-sign (bignum-sign v)))
  84.     (normalize-integer
  85.      (cond ((fxn= u-sign v-sign)
  86.             (set-bignum-sign! (add-magnitudes u v) u-sign))
  87.            ((bignum-magnitude-less? v u)
  88.             (set-bignum-sign! (subtract-magnitudes u v) u-sign))
  89.            (else
  90.             (set-bignum-sign! (subtract-magnitudes v u)
  91.                              (fixnum-negate u-sign)))))))
  92.  
  93. (define-integrable (bignum-multiply-sign u v)
  94.   (if (fx= (bignum-sign u) (bignum-sign v)) 1 -1))
  95.  
  96. (define (bignum-multiply u v)
  97.   (normalize-integer
  98.    (set-bignum-sign! (multiply-magnitudes u v) (bignum-multiply-sign u v))))
  99.  
  100. ;;; Used only by BIGNUM-DIVIDE, BIGNUM-REMAINDER, and B-F-DIV2
  101.  
  102. (define (bignum-div2 u v)
  103.   (let ((m (bignum-compare-magnitudes u v)))
  104.     (cond ((fx= m 0)
  105.            (return (bignum-multiply-sign u v) 0))
  106.           ((fx< m 0)
  107.            (return 0 u))
  108.           (else
  109.            (receive (q r)
  110.                     (div2-magnitudes u v)
  111.              (return (normalize-integer
  112.                       (set-bignum-sign! q (bignum-multiply-sign u v)))
  113.                      (normalize-integer
  114.                       (set-bignum-sign! r (bignum-sign u)))))))))
  115.  
  116. (define (bignum-divide u v)    (receive (q r) (bignum-div2 u v) q))
  117. (define (bignum-remainder u v) (receive (q r) (bignum-div2 u v) r))
  118.  
  119. (define (b-f-add u v)      (bignum-add u (fixnum->bignum v)))
  120. (define (b-f-subtract u v) (bignum-subtract u (fixnum->bignum v)))
  121.  
  122. (define (b-f-divide u v)    (receive (q r) (b-f-div2 u v) q))
  123. (define (b-f-remainder u v) (receive (q r) (b-f-div2 u v) r))
  124.  
  125. (define (fixnum-ashl-bignum num amount)
  126.   (bignum-ashl (fixnum->bignum num) amount))   ; Fix later
  127.  
  128. (define (bignum-ashr-fixnum src amount)
  129.   (normalize-integer (bignum-ashr src amount)))
  130.  
  131. ;;; Total randomness: negate, odd?, howlong.
  132.  
  133. (define (bignum-negate num)
  134.   (let ((new (copy-bignum num)))
  135.     (bignum-negate! new)
  136.     (normalize-integer new)))
  137.  
  138. (define (bignum-odd? num)
  139.   (fixnum-odd? (bignum-digit num 0)))
  140.  
  141. (define (bignum-howlong num)
  142.   (let ((last (fx- (bignum-length num) 1)))
  143.     (fx+ (fixnum-howlong (bignum-digit num last))
  144.          (fx* last *bits-per-hyperdigit*))))
  145.  
  146. ;;; MAGN is a fixnum interpreted as an unsigned  integer that is 
  147. ;;; *bits-per-fixnum* long.  EXTRA-BIT? is a handy N+1st bit for those
  148. ;;; times that you have (+ *bits-per-fixnum* 1) bits of magnitude.
  149.  
  150. (define (sign&magnitude->bignum sign extra-bit? magn)
  151.   (let ((num (create-bignum (if extra-bit? 2 1))))
  152.     (set (bignum-digit num 0) magn)
  153.     (if extra-bit? (set (bignum-digit num 1) 1))
  154.     (set-bignum-sign! num sign)
  155.     num))
  156.  
  157. ;;; Normalization:
  158.  
  159. ;;; Convert an integer to normal form.  That is, if it is a bignum within
  160. ;;; the fixnum range, convert it to a fixnum.
  161.  
  162. (define (normalize-integer n)
  163.   (cond ((fixnum? n) n)
  164.         ((if (bignum-positive? n)
  165.              (bignum-less? most-positive-fixnum-as-bignum n)
  166.              (bignum-less? n most-negative-fixnum-as-bignum))
  167.          n)
  168.         (else (bignum->fixnum n))))
  169.  
  170. (define-constant most-positive-fixnum-as-bignum
  171.   (sign&magnitude->bignum  1 nil most-positive-fixnum))
  172.  
  173. (define-constant most-negative-fixnum-as-bignum
  174.   (sign&magnitude->bignum -1 nil most-negative-fixnum))
  175.  
  176.  
  177. ;;; Coercion routines:
  178.  
  179. (define (fixnum->bignum fx)
  180.   (cond ((fx= fx most-negative-fixnum) most-negative-fixnum-as-bignum)
  181.         (else (sign&magnitude->bignum (if (fx< fx 0) -1 1) 
  182.                       nil
  183.                       (fixnum-abs fx)))))
  184.  
  185. (define (bignum->fixnum bn)
  186.   (cond ((bignum-equal? most-negative-fixnum-as-bignum bn) most-negative-fixnum)
  187.         ((bignum-positive? bn) (bignum-digit bn 0))
  188.         (else
  189.          (fx- 0 (bignum-digit bn 0)))))
  190.  
  191. (define (bignum->flonum x)
  192.   (let ((len (bignum-length x))
  193.     (radix (expt 2.0 *bits-per-hyperdigit*)))
  194.     (iterate loop ((i (fx- len 1)) (mag 0.0))
  195.       (if (fx< i 0)
  196.       (fl* mag (fixnum->flonum (bignum-sign x)))
  197.       (loop (fx- i 1)
  198.         (fl+ (fl* mag radix)
  199.              (let ((digit (bignum-digit x i)))
  200.                (if (fx< digit 0)
  201.                (fl+ (fixnum->flonum digit) radix)
  202.                (fixnum->flonum digit)))))))))
  203.  
  204. ;;; Input and output:
  205. ;++ This can be speeded up if necessary.
  206.  
  207. (define (print-bignum num port)
  208.   (let ((new-num (normalize-integer num)))
  209.     (cond ((neq? num new-num)
  210.            (format port "#{Unnormalized-bignum~_~S}"
  211.                    new-num))
  212.           (else
  213.            (let ((buffer (bignum->buffer num)))
  214.              (cond ((not (bignum-positive? num)) (writec port negative-sign-char)))
  215.              (do ((i (fx- (buffer-length buffer) 1) (fx- i 1)))
  216.                  ((not (char= (buffer-elt buffer i) #\0))
  217.                   (do ((i i (fx- i 1)))
  218.                       ((fx< i 0) (release-buffer buffer))
  219.                     (writec port (buffer-elt buffer i))))))))))
  220.  
  221. ;;; Convert a bignum to a sequence of characters.
  222. ;;; Characters are generated in reverse order by successive divisions.
  223.  
  224. (define (bignum->buffer num)
  225.   (let* ((radix (rt-radix *print-table*))
  226.          (k (\#chars-in-bit-field radix *bits-per-hyperdigit*))
  227.          (radix^k (fixnum-expt radix k))
  228.          (buffer (get-buffer)))
  229.     (iterate loop ((num num))
  230.       (receive (q r)
  231.                (b-f-div2-unnormalized num radix^k)
  232.         (output-bignum-digit (fixnum-abs r) k buffer radix)
  233.         (cond ((fx> (bignum-length q) 1)
  234.                (loop q))
  235.               (else
  236.                (iterate loop ((n (bignum-digit q 0)))
  237.                  (if (fx= n 0)
  238.                      buffer
  239.                      (loop (output-bignum-digit n k buffer radix))))))))))
  240.  
  241. ;;; Generate k digits of output.  Returns the k+1'th digit.
  242.  
  243. (define (output-bignum-digit digit k buffer radix)
  244.   (iterate loop ((n digit)
  245.                  (i k))
  246.     (cond ((fx> i 0)
  247.            (receive (q r)
  248.                     (%digit-divide 0 n radix)
  249.              (vm-write-char buffer (digit->char r radix))
  250.              (loop q (fx- i 1))))
  251.           (else n))))
  252.  
  253. ;;; Number of characters in RADIX that can surely fit in FIELD-SIZE bits.
  254. ;;; Make this more accurate.  How does BIGNUM-STRINGIFY work and did
  255. ;;; I screw it up by changing this routine?
  256.  
  257. (define (\#chars-in-bit-field radix field-size)
  258.   (fx/ field-size (fixnum-howlong radix)))
  259.  
  260. ;;; Convert string to fixnum or bignum, as appropriate.
  261.  
  262. (define (string->integer string radix)
  263.   (cond ((char= (char string) negative-sign-char)
  264.          (string->integer-aux string 1 t radix))
  265.         ((char= (char string) positive-sign-char)
  266.          (string->integer-aux string 1 nil radix))
  267.         (else
  268.          (string->integer-aux string 0 nil radix))))
  269.  
  270. ;;;  We grab a bunch of digits at a whack, convert them to fixnum, and
  271. ;;;  do multiplications just with them.  
  272. ;;; grabsize:  number of digits we can grab (whack size)
  273. ;;; shift:     radix of grabsize considered as a hyperdigit
  274. ;;; leftovers: number of digits that don't fit into an even number of grabs -
  275. ;;;            convert these first
  276.  
  277. ;;; Fast enough?  Clean enough?
  278. ;;; Hack sign inside loop rather than after so that we read most-negative-fixnum
  279. ;;; as a fixnum and not a bignum.
  280. ;;; Note that any compiler worth its salt will integrate the definitions
  281. ;;; of my+ and my*.
  282.  
  283. (define (string->integer-aux string start neg? radix)
  284.   (let ((length (string-length string))
  285.         (grabsize (\#chars-in-bit-field radix *u-bits-per-fixnum*))
  286.         (my* (lambda (x y) (cond ((fixnum? x) (fixnum-multiply-carefully x y))
  287.                                  (else (b-f-multiply x y)))))
  288.         (my+ (lambda (x y) (cond ((fixnum? x) (fixnum-add-carefully x y))
  289.                                  (else (b-f-add x y))))))
  290.     (let ((shift (fixnum-expt radix grabsize))
  291.           (leftovers (fixnum-remainder (fx- length start) grabsize)))
  292.       (let ((sum (string->fixnum string start leftovers radix)))
  293.         (do ((sum (if neg? (fixnum-negate sum) sum)
  294.                   (my+ (my* sum shift) 
  295.                        (let ((x (string->fixnum string strpos grabsize radix)))
  296.                          (if neg? (fixnum-negate x) x))))
  297.              (strpos (fx+ start leftovers)
  298.                      (fx+ strpos grabsize)))
  299.             ((fx>= strpos length) sum))))))
  300.  
  301.  
  302. ;;; This belongs elsewhere
  303.  
  304. (define (string->fixnum string start count radix)
  305.   (let ((limit (fx+ start count)))
  306.     (do ((i start (fx+ i 1))
  307.          (sum 0 (fx+ (fx* sum radix) (%char->digit (nthchar string i) radix))))
  308.         ((fx>= i limit) sum))))
  309.  
  310.  
  311. ;;; Debugging utility:
  312.  
  313. ;(define-syntax (bignum-pig x)
  314. ;  `(',*bignum-pig (lambda () ,x)))
  315.  
  316. (define (*bignum-pig x)
  317.   (let ((b1 *bignum-cons-counter*)
  318.         (b2 *bignum-cons-size-counter*))
  319.     (let ((val (x))
  320.           (a1 *bignum-cons-counter*)
  321.           (a2 *bignum-cons-size-counter*))
  322.       `(count = ,(fx- a1 b1) total = ,(fx- a2 b2) value = ,val))))
  323.  
  324. (set *bignums-print-nicely?* t)
  325.